from typing import Dict

import openstack

from app.common.base_manager import BaseManager
from utils.crypto import decrypt_pw
from tools import cache


class OpenstackError(Exception):
    pass


class OpenstackManager(BaseManager):

    Error = OpenstackError
    conn = None

    def __init__(self, config: Dict, project_id: str = None, is_abort=True):
        super(OpenstackManager, self).__init__(is_abort=is_abort)

        auth_config = decrypt_pw(config["auth"])
        auth_config["project_id"] = project_id or auth_config["project_id"]
        try:
            self.conn = openstack.connect(
                auth=auth_config, auth_type=config["auth_type"]
            )
            # 解决连接无法捕获异常问题
            self.conn.authorize()
        except:  # noqa
            self.error_func("无法连接到云计算服务!")

    def get_vm_max_rate_info(self) -> Dict:
        r = self.conn.compute.get_limits()["absolute"].toDict()
        data = {
            "instance": {
                "maxInstances": r["instances"],
                "usedInstances": r["instances_used"],
            },
            "vcpu": {"maxCores": r["total_cores"], "usedCores": r["total_cores_used"]},
            "mem": {"maxMem": r["total_ram"], "usedMem": r["total_ram_used"]},
        }
        return data

    def get_all_servers(self, details=True):
        return self.conn.compute.servers(details=details)

    @cache.cached(timeout=60)
    def get_server_id_to_info(self):
        r = {}
        for server in self.get_all_servers(details=True):
            _ip = []
            for _, v in server.addresses.items():
                _ip.append(v[0].get('addr'))

            r[server.id] = {
                'status': server.status,
                'ip': ', '.join(_ip)
            }
        return r

    def get_all_flavors(self):
        return self.conn.compute.flavors()

    def find_flavor(self, name_or_id):
        return self.conn.compute.find_flavor(name_or_id=name_or_id)

    def create_image(self, name, file_name=None, os_type="linux", **kwargs):
        image = self.conn.create_image(name, filename=file_name, **kwargs)
        image = self.conn.get_image(image.id)
        meta = {"hw_disk_bus": "ide", "hw_if_model": "e1000", "hw_video_model": "qxl"}
        if os_type != "linux":
            meta["os_type"] = os_type
        self.conn.update_image_properties(image=image, meta=meta)
        return image

    def delete_image(self, name_or_id, **kwargs):
        return self.conn.delete_image(name_or_id, **kwargs)

    def get_server_or_error(self, server_id):
        if not server_id:
            self.error_func("未提供虚拟机ID!")

        server = self.conn.compute.find_server(server_id)
        if not server:
            self.error_func("虚拟机不存在!")
        return server

    def stop_server(self, server):
        """
        关闭虚机
        """
        self.conn.compute.stop_server(server.id)
        self.conn.compute.wait_for_server(server, "SHUTOFF")

    def admin_get_hypervisors(self, filters=None, to_dict_detail=False):
        """
        获取管理主机(需要管理员权限)
        """
        r = self.conn.list_hypervisors(filters=filters or {})
        if to_dict_detail:
            _r = []
            for hypervisor in r:
                _r.append(
                    dict(  # noqa
                        name=hypervisor.name,
                        host_ip=hypervisor.host_ip,
                        status=hypervisor.status,
                        local_disk_used=hypervisor.local_disk_used,  # 储存 1:1
                        local_disk_size=hypervisor.local_disk_size,
                        memory_used=hypervisor.memory_used,
                        memory_size=hypervisor.memory_size * 1.5,  # 内存 1.5:1
                        vcpus_used=hypervisor.vcpus_used,
                        vcpus=hypervisor.vcpus * 16,  # cpu 16:1
                    )
                )
            r = _r
        return r
